package org.convallaria.infrastruct.db.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StopWatch;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ForkJoinPool;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 数据库性能优化工具类
 * 
 * 提供批量操作、并行处理、性能监控等优化功能
 * 
 * @author gjh
 * @since 2024-09-24
 */
@Slf4j
public class PerformanceOptimizer {

    private static final int DEFAULT_BATCH_SIZE = 1000;
    private static final int DEFAULT_PARALLEL_THRESHOLD = 100;

    /**
     * 批量处理数据
     * 
     * @param dataList 数据列表
     * @param batchSize 批次大小
     * @param processor 处理器
     * @param <T> 数据类型
     */
    public static <T> void batchProcess(List<T> dataList, int batchSize, Consumer<List<T>> processor) {
        if (dataList == null || dataList.isEmpty()) {
            return;
        }

        StopWatch stopWatch = new StopWatch("BatchProcess");
        stopWatch.start();

        try {
            int totalSize = dataList.size();
            int processedCount = 0;

            for (int i = 0; i < totalSize; i += batchSize) {
                int endIndex = Math.min(i + batchSize, totalSize);
                List<T> batch = dataList.subList(i, endIndex);
                
                try {
                    processor.accept(batch);
                    processedCount += batch.size();
                    
                    if (log.isDebugEnabled()) {
                        log.debug("批量处理进度: {}/{} ({:.1f}%)", 
                            processedCount, totalSize, 
                            (double) processedCount / totalSize * 100);
                    }
                } catch (Exception e) {
                    log.error("批量处理第{}批数据时发生异常", (i / batchSize + 1), e);
                    throw new RuntimeException("批量处理失败", e);
                }
            }
        } finally {
            stopWatch.stop();
            log.info("批量处理完成 - 总数: {}, 批次大小: {}, 耗时: {}ms", 
                dataList.size(), batchSize, stopWatch.getTotalTimeMillis());
        }
    }

    /**
     * 批量处理数据（使用默认批次大小）
     */
    public static <T> void batchProcess(List<T> dataList, Consumer<List<T>> processor) {
        batchProcess(dataList, DEFAULT_BATCH_SIZE, processor);
    }

    /**
     * 并行批量处理数据
     * 
     * @param dataList 数据列表
     * @param batchSize 批次大小
     * @param processor 处理器
     * @param executor 执行器
     * @param <T> 数据类型
     */
    public static <T> void parallelBatchProcess(List<T> dataList, int batchSize, 
                                               Consumer<List<T>> processor, Executor executor) {
        if (dataList == null || dataList.isEmpty()) {
            return;
        }

        if (dataList.size() < DEFAULT_PARALLEL_THRESHOLD) {
            // 数据量小时使用串行处理
            batchProcess(dataList, batchSize, processor);
            return;
        }

        StopWatch stopWatch = new StopWatch("ParallelBatchProcess");
        stopWatch.start();

        try {
            int totalSize = dataList.size();
            int batchCount = (totalSize + batchSize - 1) / batchSize;

            // 创建并行任务
            List<CompletableFuture<Void>> futures = 
                java.util.stream.IntStream.range(0, batchCount)
                    .mapToObj(i -> {
                        int startIndex = i * batchSize;
                        int endIndex = Math.min(startIndex + batchSize, totalSize);
                        List<T> batch = dataList.subList(startIndex, endIndex);
                        
                        return CompletableFuture.runAsync(() -> {
                            try {
                                processor.accept(batch);
                                log.debug("并行批次 {} 处理完成, 数据量: {}", i + 1, batch.size());
                            } catch (Exception e) {
                                log.error("并行批次 {} 处理失败", i + 1, e);
                                throw new RuntimeException("并行批量处理失败", e);
                            }
                        }, executor);
                    })
                    .collect(Collectors.toList());

            // 等待所有任务完成
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

        } finally {
            stopWatch.stop();
            log.info("并行批量处理完成 - 总数: {}, 批次大小: {}, 耗时: {}ms", 
                dataList.size(), batchSize, stopWatch.getTotalTimeMillis());
        }
    }

    /**
     * 并行批量处理数据（使用默认参数）
     */
    public static <T> void parallelBatchProcess(List<T> dataList, Consumer<List<T>> processor) {
        parallelBatchProcess(dataList, DEFAULT_BATCH_SIZE, processor, ForkJoinPool.commonPool());
    }

    /**
     * 并行转换数据
     * 
     * @param dataList 输入数据列表
     * @param transformer 转换函数
     * @param <T> 输入类型
     * @param <R> 输出类型
     * @return 转换后的数据列表
     */
    public static <T, R> List<R> parallelTransform(List<T> dataList, Function<T, R> transformer) {
        if (dataList == null || dataList.isEmpty()) {
            return List.of();
        }

        StopWatch stopWatch = new StopWatch("ParallelTransform");
        stopWatch.start();

        try {
            if (dataList.size() < DEFAULT_PARALLEL_THRESHOLD) {
                // 数据量小时使用串行处理
                return dataList.stream()
                    .map(transformer)
                    .collect(Collectors.toList());
            }

            // 并行处理
            return dataList.parallelStream()
                .map(item -> {
                    try {
                        return transformer.apply(item);
                    } catch (Exception e) {
                        log.error("并行转换数据时发生异常: {}", item, e);
                        throw new RuntimeException("数据转换失败", e);
                    }
                })
                .collect(Collectors.toList());

        } finally {
            stopWatch.stop();
            log.debug("并行转换完成 - 数据量: {}, 耗时: {}ms", 
                dataList.size(), stopWatch.getTotalTimeMillis());
        }
    }

    /**
     * 性能监控执行
     * 
     * @param taskName 任务名称
     * @param task 任务
     * @param <T> 返回类型
     * @return 任务结果
     */
    public static <T> T withPerformanceMonitoring(String taskName, java.util.function.Supplier<T> task) {
        StopWatch stopWatch = new StopWatch(taskName);
        stopWatch.start();

        try {
            T result = task.get();
            return result;
        } catch (Exception e) {
            log.error("任务 {} 执行失败", taskName, e);
            throw e;
        } finally {
            stopWatch.stop();
            long executionTime = stopWatch.getTotalTimeMillis();
            
            if (executionTime > 1000) {
                log.warn("⚠️ 任务 {} 执行较慢: {}ms", taskName, executionTime);
            } else {
                log.debug("✅ 任务 {} 执行完成: {}ms", taskName, executionTime);
            }
        }
    }

    /**
     * 无返回值的性能监控执行
     */
    public static void withPerformanceMonitoring(String taskName, Runnable task) {
        withPerformanceMonitoring(taskName, () -> {
            task.run();
            return null;
        });
    }

    /**
     * 获取推荐的批次大小
     * 
     * @param totalSize 总数据量
     * @param availableMemory 可用内存（字节）
     * @param itemSize 单个数据项大小（字节）
     * @return 推荐的批次大小
     */
    public static int getRecommendedBatchSize(int totalSize, long availableMemory, int itemSize) {
        // 基于内存限制计算批次大小
        long memoryBasedBatchSize = availableMemory / itemSize / 4; // 使用1/4可用内存
        
        // 基于数据量计算批次大小
        int sizeBasedBatchSize = Math.max(totalSize / 100, 100); // 至少100条，最多总数的1%
        
        // 取两者的最小值，但不超过默认最大值
        int recommendedSize = (int) Math.min(memoryBasedBatchSize, sizeBasedBatchSize);
        recommendedSize = Math.min(recommendedSize, DEFAULT_BATCH_SIZE);
        recommendedSize = Math.max(recommendedSize, 100); // 最小100条
        
        log.debug("推荐批次大小: {} (总数: {}, 内存限制: {}, 数据大小: {})", 
            recommendedSize, totalSize, memoryBasedBatchSize, sizeBasedBatchSize);
        
        return recommendedSize;
    }

    /**
     * 检查是否应该使用并行处理
     * 
     * @param dataSize 数据大小
     * @param processingComplexity 处理复杂度 (1-10)
     * @return 是否使用并行处理
     */
    public static boolean shouldUseParallelProcessing(int dataSize, int processingComplexity) {
        // 复杂度越高，越适合并行处理
        int threshold = DEFAULT_PARALLEL_THRESHOLD / Math.max(processingComplexity, 1);
        boolean useParallel = dataSize > threshold;
        
        log.debug("并行处理建议: {} (数据量: {}, 复杂度: {}, 阈值: {})", 
            useParallel ? "是" : "否", dataSize, processingComplexity, threshold);
        
        return useParallel;
    }

    /**
     * 内存使用情况监控
     */
    public static void logMemoryUsage(String context) {
        Runtime runtime = Runtime.getRuntime();
        long totalMemory = runtime.totalMemory();
        long freeMemory = runtime.freeMemory();
        long usedMemory = totalMemory - freeMemory;
        long maxMemory = runtime.maxMemory();
        
        double usagePercent = (double) usedMemory / maxMemory * 100;
        
        if (usagePercent > 80) {
            log.warn("🚨 内存使用率过高 [{}]: {:.1f}% ({}/{}MB)", 
                context, usagePercent, 
                usedMemory / 1024 / 1024, maxMemory / 1024 / 1024);
        } else if (log.isDebugEnabled()) {
            log.debug("📊 内存使用情况 [{}]: {:.1f}% ({}/{}MB)", 
                context, usagePercent,
                usedMemory / 1024 / 1024, maxMemory / 1024 / 1024);
        }
    }
}
